package com.neulogistics.contrib.warehouse.api.application.command.handler;

import com.blackto.lib.mediat.unicast.IRequestHandler;
import com.neulogistics.contrib.warehouse.api.application.command.ReturnInboundCommand;
import com.neulogistics.contrib.warehouse.api.application.integrationevent.WarehouseIntegrationEventService;
import com.neulogistics.contrib.warehouse.api.application.integrationevent.publish.SubstationReturnInboundIntegrationEvent;
import com.neulogistics.contrib.warehouse.api.service.ThreadLocalService;
import com.neulogistics.contrib.warehouse.domain.model.aggregate.CirculationOrder;
import com.neulogistics.contrib.warehouse.domain.model.aggregate.Inventory;
import com.neulogistics.contrib.warehouse.domain.model.aggregate.Warehouse;
import com.neulogistics.contrib.warehouse.domain.model.enumeration.CirculationDetail;
import com.neulogistics.contrib.warehouse.domain.model.enumeration.CirculationStatus;
import com.neulogistics.contrib.warehouse.domain.model.enumeration.CirculationType;
import com.neulogistics.contrib.warehouse.domain.model.enumeration.WarehouseLevel;
import com.neulogistics.contrib.warehouse.domain.model.valueobject.GoodsItem;
import com.neulogistics.contrib.warehouse.domain.repository.ICirculationOrderRepository;
import com.neulogistics.contrib.warehouse.domain.repository.IInventoryRepository;
import com.neulogistics.contrib.warehouse.domain.repository.IWarehouseRepository;
import com.neulogistics.infrastructure.cache.idservice.RedisIdUtil;
import com.neulogistics.infrastructure.common.annotation.ProduceEvent;
import com.neulogistics.infrastructure.integrationeventlog.model.IntegrationEventConstant;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;

@Component
public class ReturnInboundCommandHandler implements IRequestHandler<ReturnInboundCommand, Boolean> {
    @Resource
    ICirculationOrderRepository circulationOrderRepository;

    @Resource
    IInventoryRepository inventoryRepository;

    @Resource
    private RedisIdUtil redisIdUtil;

    @Resource
    RedissonClient redissonClient;

    @Resource
    IWarehouseRepository warehouseRepository;

    @Resource
    private TransactionTemplate transactionTemplate;

    @Resource
    private WarehouseIntegrationEventService warehouseIntegrationEventService;

    private final String Lock_Name_Prefix;

    private final String CirculationOrder_Id_Key;

    public ReturnInboundCommandHandler(@Value("${spring.application.name}") String appName) {
        Lock_Name_Prefix = "lock:" + appName +":Inventory:";
        CirculationOrder_Id_Key = appName + ":" + "circulationOrderId";
    }

    @Override
    @ProduceEvent
    public Boolean handle(ReturnInboundCommand command) throws Exception {
        // 根据入库单id查询对应实体
        CirculationOrder circulationOrder = circulationOrderRepository.findById(command.circulationId);

        // 上锁！
        String lockName = Lock_Name_Prefix + command.warehouseId + ":" + circulationOrder.getGoodsItems().get(0).getGoodsId();
        RLock lock = redissonClient.getLock(lockName);
        lock.lock();

        try {
            // 查询到对应库存
            GoodsItem goodsItem = circulationOrder.getGoodsItems().get(0);
            Inventory inventory = inventoryRepository.findByWarehouseAndGoods(command.getWarehouseId(), goodsItem.getGoodsId());

            // 退货入库：加库存
            inventory.returnInbound(goodsItem.getCount());

            // 入库单状态置为已完成
            circulationOrder.setCirculationStatus(CirculationStatus.Completed);

            // 有入就有出，生成退货出库单
            Long circulationId = redisIdUtil.nextId(CirculationOrder_Id_Key);
            CirculationOrder returnOutboundOrder = new CirculationOrder(circulationId, command.warehouseId,
                    CirculationType.Outbound, CirculationDetail.Return_Outbound,
                    circulationOrder.getTransferId(), circulationOrder.getGoodsItems());
            returnOutboundOrder.setTaskId(circulationOrder.getTaskId());

            // 持久化
            transactionTemplate.execute(new TransactionCallbackWithoutResult() {
                @Override
                protected void doInTransactionWithoutResult(TransactionStatus status) {
                try {
                    inventoryRepository.save(inventory);

                    circulationOrderRepository.save(circulationOrder);
                    circulationOrderRepository.save(returnOutboundOrder);

                    // 如果是分站的退货入库，发布分站退货入库事件，任务单置为已完成
                    Warehouse warehouse = warehouseRepository.findById(circulationOrder.getWarehouseId());
                    if(warehouse.getWarehouseLevel() == WarehouseLevel.Substation){
                        Long eventId = redisIdUtil.nextId(IntegrationEventConstant.Integration_Event_Id_Key);
                        SubstationReturnInboundIntegrationEvent substationReturnInboundIntegrationEvent =
                                new SubstationReturnInboundIntegrationEvent(eventId);
                        substationReturnInboundIntegrationEvent.setTaskId(circulationOrder.getTaskId());
                        warehouseIntegrationEventService.addAndSaveEvent(substationReturnInboundIntegrationEvent, ThreadLocalService.transactionId.get());
                    }
                } catch (Exception e){
                    // 触发回滚
                    throw new RuntimeException(e.getCause().getMessage());
                }
                }
            });
        } finally {
            lock.unlock();
        }

        return Boolean.TRUE;
    }
}
